package conf

import (
	"context"
	"errors"
	"fmt"
	clientv3 "go.etcd.io/etcd/client/v3"
	"strings"
	"time"
)

const etcd_dial_timeout = 5 * time.Second

var (
	//ErrNotUseEtcd   = errors.New("not use etcd")
	DefaultEtcdAddr = []string{"etcd:2379"}
)

func get_etcd_addrs() []string {
	return strings.Split(GetStringd("etcd.addr", ""), ",")
}

func try_load_etcd_config(codec string) (bool, error) {
	ok := false
	endpoints := get_etcd_addrs()
	if len(endpoints) == 1 && endpoints[0] == "" {
		return false, nil
	}
	config := clientv3.Config{
		Endpoints:   endpoints,
		DialTimeout: etcd_dial_timeout,
		Username:    GetStringd("etcd.username", ""),
		Password:    GetStringd("etcd.password", ""),
	}

	cli, err := clientv3.New(config)
	if err != nil {
		return ok, err
	}

	if err := get_etcd_health(cli); err != nil {
		return ok, err
	}
	setEtcdCli(cli)
	go load_etcd_config(cli)
	return true, nil
}
func load_etcd_config(cli *clientv3.Client) {
	prefix := GetStringd("etcd.prefix", "/")
load_etcd_config:
	v, err := cli.Get(context.Background(), prefix, clientv3.WithPrefix())
	if err != nil {
		fmt.Println(fmt.Sprintf("[Warn] load_etcd_config error %+v", err))
		time.Sleep(10 * time.Second)
		goto load_etcd_config
	}
	for _, t := range v.Kvs {
		configuration.SetConfig(hand_etcd_key(prefix, string(t.Key)), string(t.Value))
	}

	// 监听
	wc := cli.Watch(context.Background(), prefix, clientv3.WithPrefix())
	for {
		select {
		case v := <-wc:
			if v.Created {
				continue
			}
			if v.Canceled || v.Err() != nil {
				fmt.Println(fmt.Sprintf("[Warn] etcd watcher error %+v", v.Err()))
				time.Sleep(10 * time.Second)
				goto load_etcd_config
			}
			for _, t := range v.Events {
				fmt.Println(fmt.Sprintf("[Info] etcd updated event_type=%s key=%s prev_value=%s", t.Type, string(t.Kv.Key), string(t.PrevKv.Value)))
				switch t.Type {
				case clientv3.EventTypePut:
					configuration.SetConfig(hand_etcd_key(prefix, string(t.Kv.Key)), string(t.Kv.Value))
				case clientv3.EventTypeDelete:
					// todo 配置的删除
				}

			}
		}
	}
}
func get_etcd_health(cli *clientv3.Client) error {
	err_node_count := 0
	// 如果有n/2+1个节点异常，则return false
	for _, t := range cli.Endpoints() {
		_, err := cli.Status(context.Background(), t)
		if err != nil {
			err_node_count = err_node_count + 1
			continue
		}
	}
	if err_node_count >= len(cli.Endpoints())/2+1 {
		return errors.New("etcd异常")
	}
	return nil
}
func hand_etcd_key(prev, key string) string {
	key = strings.ReplaceAll(key, prev, "")
	if key[0] == '/' {
		key = key[1:]
	}
	keys := strings.Split(key, "/")
	switch keys[0] {
	case "hosts":
		//dm_bc.host
		{
			if len(keys) == 2 {
				return keys[1] + ".host"
			}
		}
	case "groups":
		// dm_bc.appid = 123
		// 如果是本系统=dm_bc 则可以直接调用conf.GetString("appid")
		// 如果需要读取其他系统得配置，则需要加上系统名称：conf.GetString("dm_bc.appid")
		{
			if len(keys) >= 3 {
				if keys[1] == GetApplicationName() {
					return strings.Join(keys[2:], ".")
				} else {
					return strings.Join(keys[1:], ".")
				}
			}
		}
	}
	return strings.Join(keys, ".")
}

func setEtcdCli(cli *clientv3.Client) {
	configuration.SetConfig("etcd.cli", cli)
}
func GetEtcdCli() *clientv3.Client {
	v := Get("etcd.cli")
	if v == nil {
		return nil
	}
	return v.(*clientv3.Client)
}
